/*
	Compresor Gzip
    Copyright (C) 2012 Darío López Padial 
    @bukosabino

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

/**
 @file gzip.cpp
 @brief Inicio de la aplicación gzip.
 @author Dario Lopez Padial
*/

#include <iostream>
#include <cstring>
#include <fstream>
#include "codificador.h"
#include "LZcoder.h"
#include "crc32.h"

using namespace std;

int main( int argc, char * argv [] ) {

	VD v;
	crc32 crc;
	
	// Leemos el fichero a comprimir.
	ifstream entrada(argv[1]);
	if(!entrada) {
		cerr << "error: En el fichero de entrada" << endl;
		return 1;
	}
	v.Leer(entrada);

	for(unsigned int i = 0; i < v.Size(); i++) {
		crc.Add (v.Get (i));
	}

	LZ lz(v);
	VB literales_binarios;
	VB longitud_adicional;
	VB longitud_binaria;
	VB distancia_adicional;
	Codificador prueba;
	unsigned int aux_longitud;
	unsigned int aux_distancia;
	unsigned int crema=0;
	VB flujo_bits;

	// Cabecera GZIP:
	flujo_bits += static_cast < unsigned char > (0x1F);
	flujo_bits += static_cast < unsigned char > (0x8B);
	flujo_bits += static_cast < unsigned char > (0x08);
	flujo_bits += static_cast < unsigned char > (0x18);
	flujo_bits += static_cast < unsigned char > (0x00);
	flujo_bits += static_cast < unsigned char > (0x00);
	flujo_bits += static_cast < unsigned char > (0x00);
	flujo_bits += static_cast < unsigned char > (0x00);
	flujo_bits += static_cast < unsigned char > (0x04);
	flujo_bits += static_cast < unsigned char > (0xFF);

	char * nombre_fichero = argv [1];
	char * mi_nombre = const_cast < char * > ( "Dario Lopez Padial 75166723D" );
	for ( int i=0; nombre_fichero [i] != '\0'; i++ ) {
		flujo_bits += static_cast < unsigned char > ( nombre_fichero [i] );
	}
		flujo_bits += static_cast<unsigned char>('\0');
	for ( int i=0; mi_nombre [i] != '\0'; i++ ) {
		flujo_bits += static_cast < unsigned char > ( mi_nombre [i] ) ;
	}
	flujo_bits += static_cast<unsigned char>('\0');

	// Algoritmo Deflate:
	flujo_bits += true;
	flujo_bits += true;
	flujo_bits += false;

	do{
	  crema = lz.Avanza();
		if( crema < 256 ){
			literales_binarios = prueba.codigo_huffman ( crema );
			flujo_bits += literales_binarios;
		}
		else{
			if (crema >256){
				aux_longitud = prueba.longitud_codigo ( ( crema - 256 ), longitud_adicional );
				longitud_binaria = prueba.codigo_huffman ( aux_longitud );
				flujo_bits += longitud_binaria;
				longitud_adicional.Invertir ();
				flujo_bits += longitud_adicional;

				aux_distancia = prueba.distancia_huffman ( lz.Get_distancia () , distancia_adicional );
				VB distancia_binaria (aux_distancia, 5 );
				flujo_bits += distancia_binaria;
				distancia_adicional.Invertir ();
				flujo_bits += distancia_adicional;
			}
		}
	}while( crema != 256 );
	literales_binarios = prueba.codigo_huffman ( 256 );
	flujo_bits += literales_binarios;

	// Completamos el último byte.
	while ( ( flujo_bits.Size () % 8 ) != 0 ) {
		flujo_bits += false;
	}

	// Añadir la cola gzip (crc32 y tamaño).
	VB auxiliar_crc ( crc.Get ( ), 32 );
	auxiliar_crc.Invertir ();
	flujo_bits += auxiliar_crc;
	VB auxiliar_tamanio (static_cast<unsigned long> ( v.Size ()) , 32 );
	auxiliar_tamanio.Invertir ();
	flujo_bits += auxiliar_tamanio;

	// Salida.
	if (argc == 2) {
		char * nombre = argv [1];
		strcat ( nombre, ".gz" );
		ofstream salida ( nombre );
		flujo_bits.Escribir (salida);
	}
	
	if (argc == 3) {
		char * nombre_exit = argv [2];
		strcat (nombre_exit, ".gz" );
		ofstream salida ( nombre_exit );
		flujo_bits.Escribir (salida);
	}

	if ( argc > 3 ) {
		cerr << "Fallo en el fichero de entrada" << endl;
		return 1;
	}
}
